import cv2
import numpy as np
import json
import datetime as dt
import time

import matplotlib
matplotlib.use('TkAgg')  # 或尝试 'Qt5Agg'、'WXAgg'
import matplotlib.pyplot as plt

import sys,io,os
# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
segCount=40
ms=['laplacian','sobel','tenengrad']

MTF_threshold_per=0.7

currMethod=ms[1]

# text="清晰度"
# print(text.encode('utf-8').decode('utf-8'),text)

# def calculate_clarity_roi(image_path, roi_coords, method="laplacian"):
def calculate_clarity_roi(img, roi_coords, method="laplacian"):    
    """
    计算图像指定区域的清晰度
    :param image_path: 图像路径
    :param roi_coords: ROI坐标 (x1, y1, x2, y2)，对应左上角(x1,y1)、右下角(x2,y2)
    :param method: 评估方法，可选 "laplacian"（默认）、"sobel"、"tenengrad"
    :return: 清晰度指标（值越大越清晰）
    """
    # await asyncio.sleep(0.1)
    # 1. 读取图像（保留原始通道，后续按需灰度化）
    # img = cv2.imread(image_path)
    # if img is None:
    #     raise ValueError("无法读取图像，请检查路径是否正确")
    
    # 2. 提取ROI（OpenCV格式：y1:y2, x1:x2）
    x1, y1, x2, y2 = roi_coords
    # 校验ROI坐标合法性（避免超出图像尺寸）
    h, w = img.shape[:2]
    if x1 < 0 or x2 > w or y1 < 0 or y2 > h:
        raise ValueError(f"ROI坐标超出图像范围！图像尺寸：(w={w}, h={h})，ROI：{roi_coords}")
    roi = img[y1:y2, x1:x2]
    
    # 3. 转为灰度图（所有方法均需单通道输入）
    gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
    
    # 4. 根据方法计算清晰度指标
    if method == "laplacian":
        # 拉普拉斯方差法：使用3x3拉普拉斯算子（检测二阶导数）
        laplacian = cv2.Laplacian(gray_roi, cv2.CV_64F)  # 用64F避免溢出
        clarity = laplacian.var()  # 方差越大，清晰度越高
    
    elif method == "sobel":
        # Sobel梯度法：计算x+y方向梯度的均值
        sobel_x = cv2.Sobel(gray_roi, cv2.CV_64F, 1, 0, ksize=3)
        sobel_y = cv2.Sobel(gray_roi, cv2.CV_64F, 0, 1, ksize=3)
        sobel_mag = np.sqrt(sobel_x**2 + sobel_y**2)  # 梯度幅度
        clarity = np.mean(sobel_mag)  # 均值越大，清晰度越高
    
    elif method == "tenengrad":
        # Tenengrad梯度法：计算Sobel梯度的平方和均值（抗噪声更强）
        sobel_x = cv2.Sobel(gray_roi, cv2.CV_64F, 1, 0, ksize=3)
        sobel_y = cv2.Sobel(gray_roi, cv2.CV_64F, 0, 1, ksize=3)
        tenengrad = sobel_x**2 + sobel_y**2
        clarity = np.mean(tenengrad)  # 均值越大，清晰度越高
    
    else:
        raise ValueError("方法不支持！可选：'laplacian'、'sobel'、'tenengrad'")
    
    return clarity, roi  # 返回清晰度指标和ROI图像（用于可视化）

def GetImageMTF(image_path:str)->list:
    # await asyncio.sleep(0.1)
    ret=[]
    roi_coords = (0, 0, 44, 100)  # (x1, y1, x2, y2)：左上角到右下角
    method = "laplacian"  # 选择评估方法
    method=currMethod
    # segCount=5

    img=cv2.imread(image_path)
    # print(image_path)
    h,w=img.shape[:2]
    segHeight=(int)(h/segCount)

    
    
    # 2. 计算清晰度
    try:

        for i in range(0,segCount):
            roi_coords=(0,i*segHeight,w,(i+1)*segHeight)
            # clarity_score, roi =await calculate_clarity_roi(image_path, roi_coords, method)
            clarity_score, roi = calculate_clarity_roi(img, roi_coords, method)
            # print(f"{method} area:{(roi_coords[1],roi_coords[3])} 清晰度:{clarity_score:.2f}")
            # ret.append(f'{clarity_score:.2f}')
            ret.append(round(clarity_score,2))
            
    except Exception as e:
        print(f"错误：{e}")
    return ret

def append_content_to_file(filename, content, mode='a', encoding='utf-8'):
    """
    创建新文件并写入内容
    :param filename: 文件名（可包含路径）
    :param content: 写入内容（字符串或字节）
    :param mode: 写入模式 'w'-文本写入 / 'wb'-二进制写入
    :param encoding: 文本编码（默认utf-8）
    :return: 成功返回True，失败返回False
    """
    try:
        dir_path = os.path.dirname(filename)
        if dir_path and not os.path.exists(dir_path):
            os.makedirs(dir_path)
            
        # with open(filename, mode, encoding=encoding if 'b' not in mode else None) as f:
        with open(filename, mode, encoding=encoding) as f:
            f.write(content)
            f.write('\r\n')
        return True
    except Exception as e:
        print(f"文件写入失败: {str(e)}")
        return False
    

def Get_avg(lst:list):
    # 均方根
    x=np.array(lst)
    y=np.sqrt(np.mean(np.square(x)))
    return float(y)

def get_close_value(lstpart:list,threshold:float,isUp=True):
    cnt=len(lstpart)
    ret= 0 #if isUp else 4640
    if not all( i>=threshold for i in lstpart):
         abs_up=[]
         for j in lstpart:
             abs_up.append(abs(j-threshold))
         idx=abs_up.index(min(abs_up))
         if idx>=0:
            ret=round(4640*(idx/(cnt*2) if isUp else (cnt-idx)/(cnt*2)),2)
    return ret

def get_cut_len(lst:list,threshold:float)->list:
    ret=[0,4640]
    splitNum=int(len(lst)/2)
    lst_half_up=lst[0:splitNum]
    lst_half_down=lst[splitNum:]
    ret[0]=get_close_value(lst_half_up,threshold)
    ret[1]=get_close_value(lst_half_down,threshold,False)
    return ret


def write_json_file(path:str,obj:dict):
    # je=json.JSONEncoder()
    # data=je.encode(dict)
    # with open(path,'w') as f:
    #     f.write(data)
    json_str = json.dumps(obj, ensure_ascii=False, indent=4)
    with open(path, "w", encoding="utf-8-sig") as f:
        f.write(json_str)


def modify_json_file(path:str,keyName:str,val:object):
    data_dict={}
    # 从文件读取JSON
    with open(path, "r", encoding="utf-8-sig") as f:
        data_dict = json.load(f)
    
    # if data_dict.__contains__(keyName):
    #     data_dict[keyName]=val
    # else:
    data_dict[keyName]=val
    write_json_file(path,data_dict)

import asyncio

results=[]


# async def async_expensive_computation(fn:str, executor):
#     """将耗时计算函数封装为异步版本"""
#     loop = asyncio.get_event_loop()
    
#     # 在线程池中执行同步函数，避免阻塞事件循环
#     result = await loop.run_in_executor(executor, GetImageMTF, fn)
#     return result



async def async_execute(fn:str):
    # t1=dt.datetime.now()
    shortName=os.path.basename(fn).split('.')[0]
    v= GetImageMTF(fn)
    # v=await async_expensive_computation(fn)
    avg=Get_avg(v)
    results.append((shortName,v,avg))

    # await asyncio.sleep(1)
    # return (shortName,v,avg)

    # await asyncio.sleep(3)
    # time.sleep(3)
    # t2=dt.datetime.now()
    # print(f'{fn}',t1,t2)

async def exeTask(dir:str,bs:str):
    path=f'{dir}{bs}'
    fs= os.listdir(path)
    print(path,fs)
    tasks=[]
    for i in fs:
        if i.__contains__('bmp'):
            # print(f'{path}\\{i}',dt.datetime.now())
            tasks.append(asyncio.create_task(async_execute(f'{path}\\{i}')))
            # tasks.append(async_execute(f'{path}\\{i}'))

    await asyncio.wait(tasks)
    # await asyncio.gather(tasks)



async def main():
    swathPath='E:/project/DataConfigWpf/bin/Debug/net8.0-windows/swath.json'
    
    import tkinter as tk
    from tkinter import messagebox

    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口

    fig,ax=plt.subplots()
    
    xaxis=list(range(1, segCount+1,1))
    # print(xaxis)
	
    lst_avg=[]
    lst_line_name=[]
    lst_mtf=[]

    # picdir=os.path.abspath(os.curdir)+"\\"+sys.argv[1]
    # picdir="y:\\pic\\"+sys.argv[1]
	
    # print(sys.argv[0],sys.argv[1],picdir)
    
    
    import time
    
    # t1=time.localtime()
    t1=dt.datetime.now()
    

    # await async_execute('Y:/pic/10x/10x_0.bmp')
    # print(results)

    await exeTask("y:\\pic\\",sys.argv[1])
    t2=dt.datetime.now()
    print((t2-t1).seconds,len(results),__file__)
    
    # exit()

    for i in results:
        ax.plot(xaxis,i[1],label=f"{i[0]}",lw=2)

    MTF_threshold_lst=[]
    MTF_threshold_max=[]

    # map(lambda x:len(x)==5,strings)

    bestObj=max(results,key=lambda x:x[2])
    MTF_threshold=max(bestObj[1])*MTF_threshold_per
    for i in range(0,len(bestObj[1]),1):
        MTF_threshold_lst.append(MTF_threshold)
        MTF_threshold_max.append(max(bestObj[1]))

    ax.plot(xaxis,MTF_threshold_max,label=f"max value:{round(max(bestObj[1]),2)}")
    ax.plot(xaxis,MTF_threshold_lst,label=f"throhold:{round(MTF_threshold,2)}")

    cuts=get_cut_len(bestObj[1],MTF_threshold)
    modify_json_file(swathPath,f'{sys.argv[1]}_cut_area',f'{cuts[0]},{cuts[1]}')
    
    ax.text(-0.5, 0, f"best line is {bestObj[0]}\ntop should be cut:{cuts[0]}px\nbotton should be cut:{cuts[1]}px",transform=ax.get_xaxis_transform(), ha='left', va='bottom', fontsize=15,color='red',zorder=3)  
    
    fig.canvas.manager.set_window_title(f"TDI{sys.argv[1]}清晰度输出")
    ax.set_xlabel("X axis", fontsize=12) 
    ax.set_ylabel("MTF value", fontsize=12) 

    # t2=time.localtime()
    # t2=dt.datetime.now()
    # ts= (t2-t1).seconds
    # print(f'ts:{ts}')

    plt.legend(loc='upper right')
    plt.show()
    

    root.destroy()



async def asleep(val):
    await asyncio.sleep(val)
    print(f'sleep:{val}')

async def mainsleep():
    ss=[1,5,3,4,10,4]
    tasks=[]
    for i in ss:
        tasks.append(asyncio.create_task(asleep(i)))
    await asyncio.wait(tasks)

# ---------------------- 示例：计算指定区域清晰度 ----------------------
if __name__ == "__main__":

    # t1=dt.datetime.now()
    # print(t1)
    # asyncio.run(mainsleep())
    # t2=dt.datetime.now()
    # print((t2-t1).seconds,t2)
    # exit()
    asyncio.run(main())
    exit()
    # append_content_to_file(r'c:\1.txt',"10,20,30,50")
    # append_content_to_file(r'c:\1.txt',"20,10,30,50")
    # print(v,type(v[0]))
    # line=','.join(v)
    # append_content_to_file(r'c:\1.csv',line)

    # dct={'5x_cut_area':'0,0','10x_cut_area':'0,0','20x_cut_area':'0,0','50x_cut_area':'0,0'}
    # write_json_file('c:/1.json',dct)
    swathPath='E:/project/DataConfigWpf/bin/Debug/net8.0-windows/swath.json'
    

    import tkinter as tk
    from tkinter import messagebox

    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    # messagebox.showinfo("测试", "Tkinter 工作正常！")
    # root.destroy()
    # exit()

    fig,ax=plt.subplots()
    
    # plt.figure(num='TDI清晰度输出')
    # plt.xlabel("X axis", fontsize=12) 
    # plt.ylabel("MTF value", fontsize=12) 
    # xaxis=[1, 2, 3,4,5]
    xaxis=list(range(1, segCount+1,1))
    print(xaxis)
	
    lst_avg=[]
    lst_line_name=[]
    lst_mtf=[]

    # picdir=os.path.abspath(os.curdir)+"\\"+sys.argv[1]
    picdir="y:\\pic\\"+sys.argv[1]
    # picdir="y:\\pic\\20x"
	
    # print(sys.argv[0],sys.argv[1],picdir)
    
    import datetime as dt
    import time
    
    # t1=time.localtime()
    t1=dt.datetime.now()


    fs= os.listdir(picdir)
    for i in fs:
        if i.__contains__('bmp'):
            fn=i.split('.')[0]
            lst_line_name.append(fn)
            v=GetImageMTF(f"{picdir}\\{i}")
            lst_mtf.append(v)
            lst_avg.append(Get_avg(v))
            ax.plot(xaxis,v,label=f"{fn}",lw=2)
    
        
    # plt.plot(xaxis,v,label="line1")
    # plt.plot(xaxis,[1500,2235,2425,2233,1456],label="line2")
    # messagebox.showinfo("test",str(lst_avg))

    bestLineIdx=lst_avg.index(max(lst_avg))
    if bestLineIdx>=0:
        bestLine=lst_mtf[bestLineIdx]
        MTF_threshold=max(bestLine)*MTF_threshold_per
        MTF_threshold_lst=[]
        MTF_threshold_max=[]
        for i in range(0,len(bestLine),1):
            MTF_threshold_lst.append(MTF_threshold)
            MTF_threshold_max.append(max(bestLine))
        ax.plot(xaxis,MTF_threshold_max,label=f"max value:{round(max(bestLine),2)}")
        ax.plot(xaxis,MTF_threshold_lst,label=f"throhold:{round(MTF_threshold,2)}")

    cuts=get_cut_len(bestLine,MTF_threshold)
    modify_json_file(swathPath,f'{sys.argv[1]}_cut_area',f'{cuts[0]},{cuts[1]}')
    
    ax.text(-0.5, 0, f"best line is {lst_line_name[bestLineIdx]}\ntop should be cut:{cuts[0]}px\nbotton should be cut:{cuts[1]}px",transform=ax.get_xaxis_transform(), ha='left', va='bottom', fontsize=15,color='red',zorder=3)  
    # transform=plt.get_xaxis_transform()
    
    # plt.figure(num='TDI清晰度输出')
    # plt.xlabel("X axis", fontsize=12) 
    # plt.ylabel("MTF value", fontsize=12) 
    
    fig.canvas.manager.set_window_title(f"TDI{sys.argv[1]}清晰度输出")
    ax.set_xlabel("X axis", fontsize=12) 
    ax.set_ylabel("MTF value", fontsize=12) 

    # t2=time.localtime()
    t2=dt.datetime.now()
    ts= (t2-t1).seconds
    print(f'ts:{ts}')

    plt.legend(loc='upper right')
    plt.show()
    

    root.destroy()
    pass
    